package com.geekydoo.commands;

import generated.TaskType;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Logger;

import javax.xml.datatype.XMLGregorianCalendar;

import com.geekydoo.InputOutputHandler;

/**
 * The purpose of this class is to support searching for a user-specified string
 * from among the titles, categories, descriptions, and document links of all
 * tasks.
 * 
 * @author Wai Yau
 */
public class SearchCommand implements Command {

	private static Logger logger = Logger.getLogger("geekydoo");

	// The list of all task objects managed by the program
	private List<TaskType> taskList;

	// Informational messages printed by the search command
	private static final String INFO_EMPTY_LIST = "Task list is empty.";
	private static final String INFO_NO_TASK_FOUND = "No tasks found with the given criteria.";
	private static final String INFO_NO_CRITERIA = "No search criteria.";

	// Prompts printed by the search command
	private static final String PROMPT_SEARCH_CRITERIA = "Keywords (space delimited): ";

	// Display output field strings
	private static final String FIELD_ID = "Task id: ";
	private static final String FIELD_TITLE = "Title: ";
	private static final String FIELD_DATE = "Date: ";
	private static final String FIELD_TIME = "Time: ";
	private static final String FIELD_CATEGORY = "Category: ";
	private static final String FIELD_DESCRIPTION = "Description: ";
	private static final String FIELD_DOCUMENTLINK = "Document Link: ";

	// Separators
	private static final String SEPARATOR_DATE = "-";
	private static final String SEPARATOR_TIME = ":";
	private static final String SEPARATOR_TASK = "--------------------------------------------------------------------";

	// Regular expression used to split up user input on whitespace
	private static final String REGEX_WHITESPACE = "\\s+";

	// Descriptive name for the empty string
	private static final String EMPTY_STRING = "";

	public void execute(List<TaskType> tasks) {
		taskList = tasks;
		if (taskList.size() == 0) {
			logger.info("Task list is empty so nothing to search for");
			InputOutputHandler.printToConsole(INFO_EMPTY_LIST, true, false);
		}

		String criteria = InputOutputHandler.getInputFromConsole(PROMPT_SEARCH_CRITERIA, true);
		if (criteria.equals(EMPTY_STRING)) {
			logger.info("No criteria to be searched for.");
			InputOutputHandler.printToConsole(INFO_NO_CRITERIA, true, false);
			return;
		}

		logger.info("The entire search string is: " + criteria);
		String[] keywords = criteria.trim().split(REGEX_WHITESPACE);
		processKeywords(keywords);
	}

	/**
	 * This method finds all tasks matching any of the user supplied search
	 * strings in the keywords argument and prints the tasks' details to the
	 * screen.
	 * 
	 * @param keywords An array of the strings to be searched for
	 */
	private void processKeywords(String[] keywords) {
		List<TaskType> selectedList = new ArrayList<TaskType>();
		ListIterator<TaskType> iterator = taskList.listIterator();

		for (int i = 0; i < keywords.length; i++) {
			iterator = taskList.listIterator();
			logger.info("Preparing to search tasks for keyword: " + keywords[i]);
			searchThroughTasklist(iterator, selectedList, keywords[i]);
		}

		if (selectedList.size() == 0) {
			logger.info("No matched task to be found.");
			InputOutputHandler.printToConsole(INFO_NO_TASK_FOUND, true, false);
		}
		else {
			ListIterator<TaskType> selectedIterator = selectedList.listIterator();
			while (selectedIterator.hasNext()) {
				printTaskDetails(selectedIterator.next());
			}
			logger.info("A list of matched tasks has been printed.");
		}
	}

	/**
	 * This method iterates over all tasks in the task list, adding tasks which
	 * match the user search string given by the keyword argument.
	 * 
	 * @param iterator An iterator over the task list
	 * @param selectedList The list of tasks matching user search strings
	 * @param keyword The string being searched for
	 */
	private void searchThroughTasklist(ListIterator<TaskType> iterator, List<TaskType> selectedList, String keyword) {
		while (iterator.hasNext()) {
			addMatchedTaskToList(iterator.next(), selectedList, keyword);
		}
	}

	/**
	 * This method adds the task argument to the selectedList argument if the
	 * task contains the keyword argument. The task is considered to contain the
	 * keyword argument if the task's title, categories, description, or
	 * document links contain the keyword argument.
	 * 
	 * @param task The task currently being searched
	 * @param selectedList The list of tasks matching user search strings
	 * @param keyword The string being search for
	 */
	private void addMatchedTaskToList(TaskType task, List<TaskType> selectedList, String keyword) {
		String title = task.getTitle();
		String category = task.getCategory();
		String description = task.getDescription();
		List<String> linkList = task.getDocumentLinks();

		ListIterator<String> linkIterator = linkList.listIterator();
		String link;
		while (linkIterator.hasNext()) {
			link = linkIterator.next();
			if (link.contains(keyword) && !selectedList.contains(task)) {
				selectedList.add(task);
				logger.info(keyword + " matched a task");
			}
		}

		boolean containKeyword =
				(title.contains(keyword) || category.contains(keyword) || description.contains(keyword));
		if (containKeyword && !selectedList.contains(task)) {
			selectedList.add(task);
			logger.info(keyword + " matched a task");
		}
	}

	/**
	 * This method prints all the details of a task.
	 * 
	 * @param aTask The task to be printed
	 */
	private void printTaskDetails(TaskType aTask) {
		InputOutputHandler.printToConsole(FIELD_ID + aTask.getId(), true, false);
		InputOutputHandler.printToConsole(FIELD_TITLE + aTask.getTitle(), true, true);
		XMLGregorianCalendar theDate = aTask.getDate();
		int day = theDate.getDay();
		int month = theDate.getMonth();
		int year = theDate.getYear();
		InputOutputHandler.printToConsole(FIELD_DATE + day + SEPARATOR_DATE + month + SEPARATOR_DATE + year, true,
				false);
		if (aTask.getTime() != null) {
			String time = aTask.getTime().toString();
			String[] timeParts = time.split(SEPARATOR_TIME);
			String hour = timeParts[0];
			String minute = timeParts[1];
			InputOutputHandler.printToConsole(FIELD_TIME + hour + SEPARATOR_TIME + minute, true, false);
		}
		String category = aTask.getCategory();
		String description = aTask.getDescription();
		List<String> linkList = aTask.getDocumentLinks();
		if (category != null && !category.equals(EMPTY_STRING)) {
			InputOutputHandler.printToConsole(FIELD_CATEGORY + category, true, true);
		}
		if (description != null && !description.equals(EMPTY_STRING)) {
			InputOutputHandler.printToConsole(FIELD_DESCRIPTION + description, true, true);
		}
		ListIterator<String> linkIterator = linkList.listIterator();
		String link = null;
		while (linkIterator.hasNext()) {
			link = linkIterator.next();
			if (link != null && !link.equals(EMPTY_STRING)) {
				InputOutputHandler.printToConsole(FIELD_DOCUMENTLINK + link, true, true);
			}
		}
		InputOutputHandler.printToConsole(SEPARATOR_TASK, true, false);
	}

	public boolean isListModified() {
		return false;
	}

	public List<TaskType> getPreExecutionList() {
		return taskList;
	}

}
